/**************************************************************************************************
 * light library                                                                                  *
 * Copyright © 2012 David Kretzmer                                                                *
 *                                                                                                *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software  *
 * and associated documentation files (the "Software"), to deal in the Software without           *
 * restriction,including without limitation the rights to use, copy, modify, merge, publish,      *
 * distribute,sublicense, and/or sell copies of the Software, and to permit persons to whom the   *
 * Software is furnished to do so, subject to the following conditions:                           *
 *                                                                                                *
 * The above copyright notice and this permission notice shall be included in all copies or       *
 * substantial portions of the Software.                                                          *
 *                                                                                                *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING  *
 * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND     *
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, *
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.        *
 *                                                                                                *
 *************************************************************************************************/

#pragma once

#include "light/utility/non_copyable.hpp"
#include "light/string/string.hpp"

#include <vector>
#include <cassert>
#include <ostream>
#include <algorithm>


//=================================================================================================
// Macros for convenience
//=================================================================================================
#define LIGHT_LOG_GENERIC(LOGGER) ::light::diagnostics::internal::log_message(&LOGGER, __FILE__, LIGHT_FUNCTION_NAME, __LINE__) + ::light::str_printf

#ifdef LIGHT_DEBUG
	#define LIGHT_LOG_DEBUG LIGHT_LOG_GENERIC(::light::g_debug)
#else
	#define LIGHT_LOG_DEBUG(...)
#endif

#define LIGHT_LOG_INFO LIGHT_LOG_GENERIC(::light::g_info)
#define LIGHT_LOG_WARNING LIGHT_LOG_GENERIC(::light::g_warning)
#define LIGHT_LOG_ERROR LIGHT_LOG_GENERIC(::light::g_error)


namespace light
{
namespace diagnostics
{
	//=============================================================================================
	// A log target can be a file, a stream or whatever you want it to be. Just derive from
	// log_target and implement the virtual methods.
	//=============================================================================================
	class log_target : private non_copyable
	{
	public:
		virtual void print(char const *name, char const *msg,
						   char const *file, char const *func, uint line) = 0;
	};


	//-------------------------------------------------------------------------
	// A log target that writes to an arbitrary output stream, for example
	// std::cout, std::fstream etc.
	//-------------------------------------------------------------------------
	class log_stream : public log_target
	{
	public:
		typedef ::std::ostream stream_type;

		// Constructor
		log_stream(stream_type &stream) :
			m_stream(stream)
		{

		}

		virtual void print(char const *name, char const *msg,
						   char const *file, char const *func, uint line)
		{
			m_stream << name << " [" << file << ":" << func << ":"
					 << line << "]: " << msg;
		}

	private:
		stream_type &m_stream;
	};

	//-------------------------------------------------------------------------
	// Common log streams
	//-------------------------------------------------------------------------
	extern log_stream std_out;   // Writes to std::cout
	extern log_stream std_log;   // Writes to std::clog
	extern log_stream std_error; // Writes to std::cerr


	//=============================================================================================
	// A logger is just a set of log targets. For example, you can create an error logger with one
	// log target writing to the terminal and one to a file.
	//=============================================================================================
	class logger : private non_copyable
	{
	public:
		// Constructor
		logger(char const *name) :
			m_name(name)
		{

		}

		// Construction from initializer list
		logger(char const *name, ::std::initializer_list<log_target*> targets) :
			m_targets(targets),
			m_name(name)
		{

		}

		// Adds a log target
		void add_target(log_target *target)
		{
			m_targets.push_back(target);
		}

		// Removes a log target
		void remove_target(log_target *target)
		{
			auto new_end = ::std::remove(m_targets.begin(), m_targets.end(), target);
			m_targets.erase(new_end, m_targets.end());
		}

		// Prints the message to all log targets
		void print(utf8_string const &msg, char const *file, char const *func, uint line)
		{
			for(auto target: m_targets)
				target->print(m_name, msg.c_str(), file, func, line);

		}

	private:
		::std::vector<log_target*> m_targets;
		char const *m_name;
	};


	//-------------------------------------------------------------------------
	// Common loggers
	//-------------------------------------------------------------------------
	extern logger g_debug;
	extern logger g_info;
	extern logger g_warning;
	extern logger g_error;


	//=============================================================================================
	//
	//=============================================================================================
	namespace internal
	{
		class log_message
		{
		public:
			log_message(logger *log, char const *file, char const *func, uint line) :
				m_log(log),
				m_file(file),
				m_func(func),
				m_line(line) {}

			void operator + (utf8_string const &msg)
			{
				m_log->print(msg, m_file, m_func, m_line);
			}

		private:
			logger *m_log;
			char const *m_file;
			char const *m_func;
			uint m_line;
		};
	}

} // namespace: diagnostics

using diagnostics::log_target;
using diagnostics::log_stream;
using diagnostics::logger;

using diagnostics::std_out;
using diagnostics::std_log;
using diagnostics::std_error;

using diagnostics::g_debug;
using diagnostics::g_info;
using diagnostics::g_warning;
using diagnostics::g_error;

} // namespace: light
